#include <QGridLayout>
#include <QListWidget>
#include <QPushButton>
#include <QMessageBox>
#include <QFont>
#include <QSizePolicy>
#include <QLabel>
#include <QMenu>
#include <QAction>
#include <QTimer>
#include "alphafield.h"
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent):
        QWidget(parent)
{
    field = new QAlphaField;
    field->setFont(QFont(tr("Sans"), 25));
    QGridLayout *layout = new QGridLayout;
    list_comp = new QListWidget;
    list_player = new QListWidget;
    comp = new QLabel;
    player = new QLabel;
    status = new QLabel(tr("Your move"));
    usr_word = new QLabel;
    warn_user = false;

    player_score = comp_score = 0;
    level = 0;

    done = new QPushButton(tr("Done"));
    newGame = new QPushButton(tr("New game"));
    layout->addWidget(player, 0, 0);
    layout->addWidget(list_player, 1, 0);
    layout->addWidget(usr_word, 0, 1, Qt::AlignCenter);
    layout->addWidget(done, 2, 0);
    layout->addWidget(field, 1, 1);
    layout->addWidget(newGame, 2, 2);
    layout->addWidget(status, 2, 1, Qt::AlignCenter);
    layout->addWidget(comp, 0, 2);
    layout->addWidget(list_comp, 1, 2);
    setLayout(layout);
    connect(done, SIGNAL(clicked()), this, SLOT(doneClicked()));
    sol = new QSolver;

    veryEasyLevel = new QAction(tr("Very Easy"), this);
    easyLevel = new QAction(tr("Easy"), this);
    mediumLevel = new QAction(tr("Medium"), this);
    hardLevel = new QAction(tr("Hard"), this);
    veryHardLevel = new QAction(tr("Very Hard"), this);

    newGameMenu = new QMenu;
    newGameMenu->addAction(veryEasyLevel);
    newGameMenu->addAction(easyLevel);
    newGameMenu->addAction(mediumLevel);
    newGameMenu->addAction(hardLevel);
    newGameMenu->addAction(veryHardLevel);

    connect(newGameMenu, SIGNAL(triggered(QAction*)), this, SLOT(newGameStart(QAction*)));

    connect(sol, SIGNAL(started()), this, SLOT(solverStarted()));
    connect(sol, SIGNAL(finished()), this, SLOT(compMove()));
    connect(field, SIGNAL(stringChanged(QString)), this, SLOT(usrWordChanged(QString)));
    connect(newGame, SIGNAL(clicked()), this, SLOT(newGameClicked()));

    srand(time(NULL));
    firstMove = true;
    compMove();
    updateScore();
    updateLevel();
}

MainWindow::~MainWindow()
{
    delete sol;
}

void MainWindow::doneClicked()
{
    if(word.size() > 2)
    {
        if(!field->isValidWord())
        {

            warn(tr("Word must include a new letter"));
            return;
        }
        const wstring &ws = word.toStdWString();
        QString tmp(QString::fromStdWString(ws));
        if(!solver::searchDict(ws))
        {
            warn(tr("Don't know this word"));
            field->restartLastConf();
            update();
            return;
        }
        if(!list_comp->findItems(tmp, Qt::MatchExactly).size() &&
               !list_player->findItems(tmp, Qt::MatchExactly).size() &&
                ws != firstWord)
        {
            list_player->addItem(QString::fromWCharArray(ws.c_str()));
            player_score += ws.size();
            updateScore();
            field->selReset();
        }
        else
        {

            warn(tr("Word is used"));

            field->restartLastConf();
            update();
            return;
        }
    }
    else
    {
        if(word.size())
        {
            warn(tr("Word's length must be >= 3"));
            field->restartLastConf();
            return;
        }
        else
        {
            int ret = QMessageBox::warning(this, tr("Banana Head"),
                                tr("Do you really want to SKIP your move?"),
                                QMessageBox::Yes | QMessageBox::No,
                                QMessageBox::No);
            switch(ret)
            {
                case QMessageBox::Yes:
                list_player->addItem(tr(" -- "));
                break;
                case QMessageBox::No:
                return;
            }

        }
    }
    sol->setField(field->getField());
    sol->start();
}

void MainWindow::solverStarted()
{
    done->setEnabled(false);
    field->setEnabled(false);
    status->setText(tr("Thinking..."));
    done->update();
    field->update();
}

void MainWindow::compMove()
{
    try {
        if(firstMove)
        {
            vector<wstring> result;
            wstring str;
            solver::getWords(result, 5);
            if(!result.size())
            {
                QMessageBox::critical(this, tr("Banana Head"),
                                      tr("Can't find a word to make a first move!\nCheck your dictionary encoding"),
                                      QMessageBox::Ok, QMessageBox::Ok);
                exit(-1);
                return;
            }
            int i = rand()%(result.size() + 1);
            memcpy(field->getField()[2], result[i].c_str(), 5*sizeof(wchar_t));
            firstWord = result[i];
            firstMove = false;
        }
        else
        {
            using namespace solver;
            const Words &res = sol->getResult();
            int levelCorrection;
            if(res.size())
            {
#ifdef __WIN32__
                levelCorrection = (level < 4 ? (rand()<<16 + rand())%((4 - level)*res.size()>>2) : 0);
#else
                levelCorrection = (level < 4 ? rand()%((4 - level)*res.size()>>2) : 0);
#endif
                size_t i;
                int j = 0;
                do {
                    for( i = levelCorrection; i < res.size(); i++)
                        {
                            QString tmp(QString::fromStdWString(res[i].first));
                            if(!list_comp->findItems(tmp, Qt::MatchExactly).size() &&
                               !list_player->findItems(tmp, Qt::MatchExactly).size() &&
                                res[i].first != firstWord)
                            break;
                        }
                        j++;
                        levelCorrection = 0;
                    } while(i == res.size() && j<2);

                    if(i < res.size())
                    {
                        comp_score += res[i].first.length();
                        list_comp->addItem(QString::fromWCharArray(res[i].first.c_str()));
                        deque<Coords>::const_iterator it;

                        int j = 0;
                            for( it = res[i].second.begin(); it != res[i].second.end(); ++it, j++)
                            field->getField()[it->y][it->x] = (res[i].first)[j];

                        field->setSel(res[i].second);
                    }
                    else throw(1);

            }
            else throw(1);
            /*{
                list_comp->addItem(tr(" -- "));
            }*/
            done->setEnabled(true);
            field->setEnabled(true);
            status->setText(tr("Your move"));
            updateScore();
            update();
        }

        //field->selReset();
        wchar_t *ptr = field->getField()[0];
        for(int i = 0; i < 25; i++)
            if(*ptr++ == L' ') return;
    }
    catch(int) {}
    //catch(...) {throw; }
    if(player_score > comp_score)
        QMessageBox::information(this, tr("Banana Head - Game Result"), tr("Player wins!"));
    else if(player_score < comp_score)
         QMessageBox::information(this, tr("Banana Head - Game Result"), tr("AI wins!"));
    else
        QMessageBox::information(this, tr("Banana Head - Game Result"), tr("Dead heat!"));
    field->fullReset();
    list_comp->clear();
    list_player->clear();
    player_score = comp_score = 0;
    firstMove = true;
    compMove();
    updateScore();
    update();
}

void MainWindow::updateScore()
{
    QString pl = tr("Player: ");
    QString ai = tr("AI: ");
    pl += QString::number(player_score);
    ai += QString::number(comp_score);
    comp->setText(ai);
    player->setText(pl);
}

void MainWindow::usrWordChanged(QString str)
{
    word = str;
    if(str.size())
        usr_word->setText(tr("Word: ")+str);
    else
        if(!warn_user)
            usr_word->setText(tr(""));
}

void MainWindow::updateLevel()
{
    QString title;
    switch(level)
    {
        case 0:
        title = tr("Very Easy");
        break;
        case 1:
        title = tr("Easy");
        break;
        case 2:
        title = tr("Medium");
        break;
        case 3:
        title = tr("Hard");
        break;
        case 4:
        title = tr("Very Hard");
        break;
    }
    setWindowTitle(tr("Banana Head - ") + title);
}

void MainWindow::newGameClicked()
{
    newGameMenu->popup(newGame->mapToGlobal(QPoint(0,0)));
}

void MainWindow::newGameStart(QAction *action)
{
    if(action == veryEasyLevel)
            level = 0;
    else
        if(action == easyLevel)
            level = 1;
    else
        if(action == mediumLevel)
            level = 2;
    else
        if(action == hardLevel)
            level = 3;
    else
            level = 4;

    sol->terminate();
    field->fullReset();
    list_comp->clear();
    list_player->clear();
    player_score = comp_score = 0;
    firstMove = true;
    compMove();
    updateScore();
    updateLevel();
    update();
}

void MainWindow::warn(const QString &text)
{
    warn_user = true;
    usr_word->setText(tr("<font color=\"red\">") + text + tr("</font>"));
    usr_word->update();
    QTimer::singleShot(2000, this, SLOT(disableWarning()));
}

void MainWindow::disableWarning()
{
    warn_user = false;
    usrWordChanged(word);
}
